#include "model.h"

Model* Model::instance = NULL;

Model::Model(QMap<QString, Clazz*>* tree)
{
    this->clazzes = *tree;

//    Clazz *Zwierze = new Clazz("Zwierze");
//    Clazz *String = new Clazz("String");
//    Clazz *Pies = new Clazz("Pies");

//    Attribute *kolor = new Attribute("kolor");
//    kolor->setClazz(*String);
//    kolor->setAccessModifier(PRIVATE);

//    Attribute *piesAttribute = new Attribute("ppies");
//    piesAttribute->setClazz(*Pies);
//    piesAttribute->setAccessModifier(PRIVATE);

//    QVector<Attribute*> zwierzeAttributes;
//    zwierzeAttributes.append(piesAttribute);

//    Zwierze->setAttributes(zwierzeAttributes);

//    QVector<Attribute*> piesAttributes;
//    piesAttributes.append(kolor);

//    Pies->setAttributes(piesAttributes);

//    Method *siad = new Method("Siad");
//    siad->setAccessModifier(PUBLIC);

//    QVector<Method*> piesMethods;
//    piesMethods.append(siad);

//    Pies->setMethods(piesMethods);

//    QVector<Clazz*> zwierzeSubtypes;
//    zwierzeSubtypes.append(Pies);
//    Zwierze->setSubtypes(zwierzeSubtypes);

//    clazzes.insert("Zwierze", Zwierze);
//    clazzes.insert("Pies", Pies);
}

Model* Model::getInstance(QMap<QString, Clazz*>* tree)
{
    if( instance == NULL ){
        instance = new Model(tree);
    }

    return instance;
}

QMap<QString, Clazz*> Model::getClazzes()
{
    return clazzes;
}

void Model::setClazzes(QMap<QString, Clazz*> clazzes)
{
    this->clazzes = clazzes;
}

QString Model::toXml()
{

    QString ret = "";
    ret += "<?xml version = \"1.0\" encoding = \"UTF-8\"?>\n" \
           "<XMI xmi.version = \"1.1\" xmlns:UML=\"href://org.omg/UML/1.3\" timestamp = \""+QDate::currentDate().toString(Qt::SystemLocaleLongDate)+"\">\n" \
           "<XMI.header>\n" \
           "  <XMI.documentation>\n" \
           "    <XMI.owner></XMI.owner>\n" \
           "    <XMI.contact></XMI.contact>\n" \
           "    <XMI.exporter>Kowalski Morytko XMI generator</XMI.exporter>\n" \
           "    <XMI.exporterVersion>1.0</XMI.exporterVersion>\n" \
           "    <XMI.notice></XMI.notice>\n" \
           "  </XMI.documentation>\n" \
           "  <XMI.metamodel xmi.name = \"UML\" xmi.version = \"1.3\"/>\n" \
           "  </XMI.header>\n" \
           "<XMI.content>\n" \
           "<UML:Model xmi.id=\"UMLProject.1\">\n" \
           "  <UML:Namespace.ownedElement>\n" \
           "    <UML:Model xmi.id=\"UMLModel.2\" name=\"Design Model\" visibility=\"public\" isSpecification=\"false\" namespace=\"UMLProject.1\" isRoot=\"false\" isLeaf=\"false\" isAbstract=\"false\">\n";

    XMLSerializable::currentNamespace = "UMLModel.2";

    QList<Clazz*> clazzList = clazzes.values();

    // dla kazdej klasy dodajemy definicje
    for( QList<Clazz*>::Iterator it = clazzList.begin(); it != clazzList.end(); it++){
        ret+= (*it)->toXml()+"\n";

        // dodawanie zaleznosci miedzy klasami

        // generalizacje
        for( int i=0; i<(*it)->getSubtypes().size(); i++)
        {
            Clazz *child = (*it)->getSubtypes()[i];
            QString parent = XMLSerializable::typeNameToXmiId.value( (*it)->getClassName() );

            QString childId = XMLSerializable::typeNameToXmiId.value( child->getClassName(), "UMLClass."+QString::number(XMLSerializable::currentNumber++) );
            XMLSerializable::typeNameToXmiId.insert(child->getClassName(), childId);

            QString generalizationId = "UMLGeneralization."+QString::number(XMLSerializable::currentNumber++);
            ret += "      <UML:Generalization xmi.id=\""+generalizationId+"\" name=\"\" visibility=\"public\" isSpecification=\"false\" namespace=\""+XMLSerializable::currentNamespace+"\" discriminator=\"\" child=\""+childId+"\" parent=\""+parent+"\"/> \n";
        }

        // asocjacje
        QSet<QString> createdAssociations;

        for(QVector<Attribute*>::Iterator attr = (*it)->getAttributes().begin(); attr != (*it)->getAttributes().end(); attr++)
        {
            Clazz clazz = (*attr)->getClazz();
            QString name = clazz.getClassName();

            // nie duplikujemy wpisow i nie robimy asocjacji do typow prymitywnych
            if(!createdAssociations.contains(name) && clazzes.contains(name)){

                // asocjacja

//               // TODO zrobic sprawdzanie czy nie powtarzaja sie asocjacje

                QString associationId = "UMLAssociation."+QString::number(XMLSerializable::currentNumber++);

                ret += "      <UML:Association xmi.id=\""+associationId+"\" name=\"\" visibility=\"public\" isSpecification=\"false\" namespace=\""+XMLSerializable::currentNamespace+"\">\n";
                ret += "        <UML:Association.connection>\n";

                // pierwsza koncowka asocjacji
                QString firstAssociationEndId = "UMLAssociationEnd."+QString::number(XMLSerializable::currentNumber++);
                QString firstClassId = XMLSerializable::typeNameToXmiId.value((*it)->getClassName());

                ret += "          <UML:AssociationEnd xmi.id=\""+firstAssociationEndId+"\" name=\"\" visibility=\"public\" isSpecification=\"false\" isNavigable=\"true\" ordering=\"unordered\" aggregation=\"none\" targetScope=\"instance\" changeability=\"changeable\" association=\""+associationId+"\" type=\""+firstClassId+"\"/>\n";

                // druga koncowka asocjacji
                QString secondAssociationEndId = "UMLAssociationEnd."+QString::number(XMLSerializable::currentNumber++);
                QString secondClassId = XMLSerializable::typeNameToXmiId.value( name, "UMLClass."+QString::number(XMLSerializable::currentNumber++) );
                XMLSerializable::typeNameToXmiId.insert(name, secondClassId);

                ret += "          <UML:AssociationEnd xmi.id=\""+secondAssociationEndId+"\" name=\"\" visibility=\"public\" isSpecification=\"false\" isNavigable=\"true\" ordering=\"unordered\" aggregation=\"none\" targetScope=\"instance\" changeability=\"changeable\" association=\""+associationId+"\" type=\""+secondClassId+"\"/>\n";

                ret += "        </UML:Association.connection>\n";
                ret += "      </UML:Association>\n";

                createdAssociations.insert(name);
            }
        }
    }

    ret += "    </UML:Model>\n";

    // dodawanie definicji typów nie uwzględnionych w diagramie klas

    for(QMap<QString, QString>::Iterator it = XMLSerializable::typeNameToXmiId.begin(); it != XMLSerializable::typeNameToXmiId.end(); it++)
    {
        if(!clazzes.contains(it.key())){
            ret += "    <UML:DataType xmi.id=\""+it.value()+"\" name=\""+it.key()+"\" visibility=\"public\" isSpecification=\"false\" isRoot=\"false\" isLeaf=\"false\" isAbstract=\"false\"/> \n";
        }
    }

    ret += "  </UML:Namespace.ownedElement>\n" \
           "</UML:Model>\n" \
           "</XMI.content>\n" \
           "</XMI>\n" ;

     return ret;

}
